package com.lingjtx.common.core.util;

import com.lingjtx.common.core.constant.DateField;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 公共的时间工具类
 *
 * @author John Liu
 */
public class DateTimeUtil {

    public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";

    public static final String SIMPLE_FORMAT = "yyyy-MM-dd HH:mm";

    public static final String GMT_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";

    public static final String TIMESTAMP = "yyyyMMddHHmmssSSS";

    public static final String FOLDER_NAME = "yyyy/MM/"; // 文件夹命名到月份

    public static final String DATE_FORMAT = "yyyy-MM-dd";

    public static final String MIN_FORMAT = "MM-dd HH:mm";

    public static final String MDH_FORMAT = "MM-dd HH时";

    public static final String MONTH_FORMAT = "yyyy年MM月";

    public static final String DAY_FORMAT = "yyyy年MM月dd日";

    public static final String MONTH_DAY_FORMAT = "MM月dd日";

    public static final String HOUR_MIN_FORMAT = "HH:mm";

    public static String date2GMT(Date date, String formatStr) {
        SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT+1"));
        return sdf.format(date);
    }

    public static Date strToDate(String dateTimeStr, String formatStr) {
        if (StringUtil.isEmpty(dateTimeStr)) return null;
        DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
        DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
        return dateTime.toDate();
    }

    public static String timestampToStr(long timestamp, String formatStr) {
        return new SimpleDateFormat(formatStr).format(timestamp);
    }

    public static String dateToStr(Date date, String formatStr) {
        if (date == null) {
            return "";
        }
        DateTime dateTime = new DateTime(date);
        return dateTime.toString(formatStr);
    }

    public static Date strToDate(String dateTimeStr) {
        return strToDate(dateTimeStr, STANDARD_FORMAT);
    }

    public static String dateToStr(Date date) {
        return dateToStr(date, STANDARD_FORMAT);
    }

    public static String getDateStr(String format) {
        return dateToStr(new Date(), format);
    }

    public static String getDateStr() {
        return dateToStr(new Date(), STANDARD_FORMAT);
    }

    public static String getTimestamp() {
        return getDateStr(TIMESTAMP);
    }

    public static String getTime() {
        return String.valueOf(new Date().getTime());
    }

    public static String getTimestampByMillis() {
        return String.valueOf(System.currentTimeMillis());
    }

    public static String getTimestampBySecond() {
        String str = getTimestampByMillis();
        if (StringUtil.isNotEmpty(str)) {
            return str.substring(0, str.length() - 3);
        }
        return null;
    }

    public static String formatGMTDate() {
        SimpleDateFormat df = new SimpleDateFormat(GMT_FORMAT);
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df.format(new Date());
    }

    /**
     * 昨天
     *
     * @return 昨天
     */
    public static Date yesterday() {
        return offsetDay(new Date(), -1);
    }

    /**
     * 明天
     *
     * @return 明天
     */
    public static Date tomorrow() {
        return offsetDay(new Date(), 1);
    }

    /**
     * 上周
     *
     * @return 上周
     */
    public static Date lastWeek() {
        return offsetWeek(new Date(), -1);
    }

    /**
     * 下周
     *
     * @return 下周
     */
    public static Date nextWeek() {
        return offsetWeek(new Date(), 1);
    }

    /**
     * 上个月
     *
     * @return 上个月
     */
    public static Date lastMonth() {
        return offsetMonth(new Date(), -1);
    }

    /**
     * 下个月
     *
     * @return 下个月
     */
    public static Date nextMonth() {
        return offsetMonth(new Date(), 1);
    }

    /**
     * 偏移毫秒数
     *
     * @param date   日期
     * @param offset 偏移毫秒数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetMillisecond(Date date, int offset) {
        return offset(date, DateField.MILLISECOND, offset);
    }

    /**
     * 偏移秒数
     *
     * @param date   日期
     * @param offset 偏移秒数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetSecond(Date date, int offset) {
        return offset(date, DateField.SECOND, offset);
    }

    /**
     * 偏移分钟
     *
     * @param date   日期
     * @param offset 偏移分钟数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetMinute(Date date, int offset) {
        return offset(date, DateField.MINUTE, offset);
    }

    /**
     * 偏移小时
     *
     * @param date   日期
     * @param offset 偏移小时数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetHour(Date date, int offset) {
        return offset(date, DateField.HOUR_OF_DAY, offset);
    }

    /**
     * 偏移天
     *
     * @param date   日期
     * @param offset 偏移天数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetDay(Date date, int offset) {
        return offset(date, DateField.DAY_OF_YEAR, offset);
    }

    /**
     * 偏移周
     *
     * @param date   日期
     * @param offset 偏移周数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetWeek(Date date, int offset) {
        return offset(date, DateField.WEEK_OF_YEAR, offset);
    }

    /**
     * 偏移月
     *
     * @param date   日期
     * @param offset 偏移月数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetMonth(Date date, int offset) {
        return offset(date, DateField.MONTH, offset);
    }

    /**
     * 获取指定日期偏移指定时间后的时间
     *
     * @param date      基准日期
     * @param dateField 偏移的粒度大小（小时、天、月等）{@link DateField}
     * @param offset    偏移量，正数为向后偏移，负数为向前偏移
     * @return 偏移后的日期
     */
    public static Date offset(Date date, DateField dateField, int offset) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(dateField.getValue(), offset);
        return cal.getTime();
    }

    /**
     * 两个时间相差多久，两个日期的绝对值
     *
     * @param start     起始时间
     * @param end       结束时间
     * @param dateField 相差的粒度大小（小时、天、月等）{@link DateField}
     * @return 相差时间
     */
    public static int differ(Date start, Date end, DateField dateField) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(start);
        int startYear = cal.get(Calendar.YEAR);
        int startMonth = cal.get(Calendar.MONTH);
        long startValue = cal.getTimeInMillis();
        cal.setTime(end);
        int endYear = cal.get(Calendar.YEAR);
        int endMonth = cal.get(Calendar.MONTH);
        long endValue = cal.getTimeInMillis();
        return switch (dateField) {
            case MILLISECOND -> (int) Math.abs(startValue - endValue);
            case SECOND -> (int) (Math.abs(startValue - endValue) / 1000);
            case MINUTE -> (int) (Math.abs(startValue - endValue) / (1000 * 60));
            case HOUR -> (int) (Math.abs(startValue - endValue) / (1000 * 3600));
            case DAY_OF_YEAR -> (int) (Math.abs(startValue - endValue) / (1000 * 3600 * 24));
            case WEEK_OF_YEAR -> (int) (Math.abs(startValue - endValue) / (1000 * 3600 * 24 * 7));
            case MONTH -> Math.abs(startYear - endYear) * 12 + Math.abs(startMonth - endMonth);
            case YEAR -> Math.abs(startYear - endYear);
            default -> 0;
        };
    }

    public static int differYear(Date start, Date end) {
        return differ(start, end, DateField.YEAR);
    }

    public static int differMonth(Date start, Date end) {
        return differ(start, end, DateField.MONTH);
    }

    public static int differDay(Date start, Date end) {
        return differ(start, end, DateField.DAY_OF_YEAR);
    }

    public static int differWeek(Date start, Date end) {
        return differ(start, end, DateField.WEEK_OF_YEAR);
    }

    public static int differHour(Date start, Date end) {
        return differ(start, end, DateField.HOUR);
    }

    public static int differMinute(Date start, Date end) {
        return differ(start, end, DateField.MINUTE);
    }

    public static int differSecond(Date start, Date end) {
        return differ(start, end, DateField.SECOND);
    }
}
